// Copyright (c) Appeon. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.

using System.Threading;
using System.Threading.Tasks;
using System.Data;
using System.Collections.Generic;
using System;

namespace SnapObjects.Data
{
    internal class BatchUpdateCommand : UpdateCommand
    {
        public BatchUpdateCommand(DataContext dataContext)
            : base(dataContext)
        {
        }

        public IAdoDbDataVisitor Execute(IReadOnlyList<IUpdatable> updatables)
        {
            this.InitCommand();

            _dataContext.CurrentConnection.Open();

            IAdoDbDataVisitor result = null;

            try
            {
                var commandBuilder = new BatchCommandBuilder(_dataContext);

                var commands = commandBuilder.BuildCommand(updatables);

                foreach (var command in commands)
                {
                    if (String.IsNullOrWhiteSpace(command.BuiltSql))
                    {
                        continue;
                    }

                    var builtSql = _dataContext.AdoDbSqlProvider.SqlGenerationHelper.SqlBegin
                        + command.BuiltSql
                        + _dataContext.AdoDbSqlProvider.SqlGenerationHelper.SqlEnd;

                    result = _adoDbProvider.ExecuteSqlQuery(builtSql, command.AdoDbParameters);

                    this.ConsumeResults(command, result);

                    result?.Dispose();
                }
            }
            catch
            {
                throw;
            }
            finally
            {
                result?.Dispose();

                _dataContext?.CurrentConnection?.Close();
            }

            return result;
        }

        public async Task<IAdoDbDataVisitor> ExecuteAsync(
            IReadOnlyList<IUpdatable> updatables, CancellationToken cancellationToken)
        {
            this.InitCommand();

            await _dataContext.CurrentConnection.OpenAsync(cancellationToken);

            IAdoDbDataVisitor result = null;

            try
            {
                var commandBuilder = new BatchCommandBuilder(_dataContext);
                
                var commands = commandBuilder.BuildCommand(updatables);

                foreach (var command in commands)
                {
                    if (String.IsNullOrWhiteSpace(command.BuiltSql))
                    {
                        continue;
                    }

                    var builtSql = _dataContext.AdoDbSqlProvider.SqlGenerationHelper.SqlBegin
                        + command.BuiltSql
                        + _dataContext.AdoDbSqlProvider.SqlGenerationHelper.SqlEnd;

                    result = await _adoDbProvider.ExecuteSqlQueryAsync(
                        builtSql, command.AdoDbParameters, cancellationToken);

                    this.ConsumeResults(command, result);

                    if (result != null)
                    {
                        await result.DisposeAsync();
                    }
                }
            }
            catch
            {
                throw;
            }
            finally
            {
                if (result != null)
                {
                    await result.DisposeAsync();
                }

                if (_dataContext != null && _dataContext.CurrentConnection != null)
                {
                    await _dataContext.CurrentConnection.CloseAsync();
                }
            }

            return result;
        }

        private void ConsumeResults(
            BatchCommandBuilderResult commandBuilderResult, IAdoDbDataVisitor dataVisitor)
        {
            foreach (var updatable in commandBuilderResult.Updatables)
            {
                if (updatable.Updatable.UpdateType == UpdateType.ModelUpdate)
                {
                    this.ConsumeResult(
                        updatable.ModelUpdateInfo, updatable.ResultSetMapping, dataVisitor);
                }
                else
                {
                    this.ConsumeResult(
                        (IUpdatableSql)updatable.Updatable, updatable.ResultSetMapping, dataVisitor);
                }

                if (updatable.ResultSetMapping != ResultSetMapping.NoResultSet)
                {
                    dataVisitor.NextResult();
                }
            }
        }
    }
}
